package com.freewater.helloworld.application;

import com.freewater.helloworld.entity.Book;
import com.freewater.helloworld.entity.Book_;
import com.freewater.helloworld.entity.Person;
import com.freewater.helloworld.entity.User;
import com.freewater.helloworld.query.Queries_;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.query.Page;
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
import org.hibernate.relational.SchemaManager;

import java.util.List;

import static java.lang.System.out;
import static org.hibernate.cfg.AvailableSettings.*;

public class HelloHibernate {
    public static void main(String[] args) {
        var sessionFactory = new Configuration()
                .addAnnotatedClass(Book.class)
                .addFile(getMapperFile("/mapper/helloworld/Person.orm.xml"))
                .addFile(getMapperFile("/mapper/helloworld/User.orm.xml"))
                // use H2 in-memory database
                .setProperty(JAKARTA_JDBC_URL, "jdbc:h2:mem:db1")
                .setProperty(JAKARTA_JDBC_USER, "sa")
                .setProperty(JAKARTA_JDBC_PASSWORD, "")
                // use Agroal connection pool
                .setProperty("hibernate.agroal.maxSize", 20)
                // display SQL in console
                .setProperty(SHOW_SQL, true)
                .setProperty(FORMAT_SQL, true)
                .setProperty(HIGHLIGHT_SQL, true)
                .buildSessionFactory();

        try (sessionFactory) {
            SchemaManager schemaManager = sessionFactory.getSchemaManager();
            // export the inferred database schema
            schemaManager.exportMappedObjects(true);
            // validates our O/R mapping annotations against the actual database schema
            schemaManager.validateMappedObjects();

            // persist an entity
            sessionFactory.inTransaction(session -> {
                session.persist(new Book("9781932394153", "Hibernate in Action"));
                session.persist(new Person(1, "freewater", 21, "chengdu handsome boy"));
                session.persist(new User(1, "yujishui", "123456"));
            });

            // query data using HQL
            sessionFactory.inSession(session -> {
                var query1 = session.createSelectionQuery("from Book", Book.class);
                out.println(query1.getSingleResult());
                var query2 = session.createSelectionQuery("from Person", Person.class);
                out.println(query2.getSingleResult());
                var query3 = session.createSelectionQuery("from User", User.class);
                out.println(query3.getSingleResult());
            });

            // query data using criteria API
            sessionFactory.inSession(session -> {
                String result = queryByCriteria(session, sessionFactory);
                out.println(result);
            });

            // query data using hql
            sessionFactory.inSession(session -> {
                Book book = queryByHql(session);
                out.println(book);
            });
        }
    }

    private static String getMapperFile(String xmlMapperFilePath) {
        java.net.URL url = HelloHibernate.class.getResource(xmlMapperFilePath);
        assert url != null;
        return url.getFile();
    }

    private static String queryByCriteria(Session session,
                                          SessionFactory sessionFactory) {
        HibernateCriteriaBuilder builder = sessionFactory.getCriteriaBuilder();
        var query = builder.createQuery(String.class);
        var book = query.from(Book.class);
        query.select(builder.concat(builder.concat(book.get(Book_.isbn),
                        builder.literal(": ")),
                book.get(Book_.title)));
        var selectionQuery = session.createSelectionQuery(query);
        return selectionQuery.getSingleResult();
    }

    private static Book queryByHql(Session session) {
        List<Book> books = Queries_.findBooksByTitleWithPagination(session, "Hibernate%", Page.first(20));
        assert books.size() == 1;
        return books.getFirst();
    }
}
